#pragma once

#include <butil/string_printf.h>

#include "doc/convert/attribute_convert/encode_length.h"
#include "index/attribute_index/attribute_mem_indexer/attribute_mem_indexer.h"
#include "index/attribute_index/attribute_mem_indexer/dump/var_len_data_dumper.h"
#include "util/radix_tree/var_len_data_accessor.h"

namespace hawking {
namespace indexlib {

template<class T>
class MultiValueAttributeMemIndexer : public AttributeMemIndexer {
public:
    MultiValueAttributeMemIndexer(int32_t field_id, util::ColumnType field_type) :
        AttributeMemIndexer(field_id, field_type) {}
    virtual ~MultiValueAttributeMemIndexer() = default;

    virtual void Init() override {
        BaseInit_();
        accessor_ = std::make_unique<VarLenDataAccessor>();
        accessor_->Init(pool_.get());
    }

    virtual uint64_t DocCount() const override {
        if (!accessor_) {
            return 0;
        }

        return accessor_->GetDocCount();
    }

    virtual Status Dump(
        const std::string& attr_dir,
        const std::unordered_map<DocId, DocId>& old2new_map) override {
        std::unique_ptr<VarLenDataDumper> dumper =
            std::make_unique<VarLenDataDumper>(accessor_.get());
        std::string offset_file = butil::string_printf("%s/offset_file", attr_dir.data());
        std::string varlen_metafile =
            butil::string_printf("%s/varlen_metafile", attr_dir.data());
        std::string data_file = butil::string_printf("%s/data_file", attr_dir.data());
        return dumper->Dump(
            field_type_, ElementLength(),
            offset_file, varlen_metafile, data_file, old2new_map);
    }

    Status BatchRead(SegmentContext* segment_ctx);

    virtual uint32_t ElementLength() const override {
        return sizeof(T);
    }

protected:
    virtual Status AddField_(const std::string_view& value) override {
        return (accessor_ == nullptr) ? Status::Uninitialize :
            (accessor_->AppendValue(value) ? Status::OK : Status::InternalError);
    }

    virtual Status UpdateField_(DocId doc_id, const std::string_view& value) override {
        return (accessor_ == nullptr) ? Status::Uninitialize :
            accessor_->UpdateValue(doc_id, value);
    }

    Status ReadOneDoc_(DocId doc_id, util::FieldValue* value) const;

private:
    std::unique_ptr<VarLenDataAccessor> accessor_;
};

template<class T>
Status MultiValueAttributeMemIndexer<T>::ReadOneDoc_(
    DocId doc_id, util::FieldValue* value) const {
    uint8_t* data = nullptr;
    accessor_->ReadData(doc_id, std::ref(data));
    if (!data) {
        return Status::NotFound;
    }

    attribute_convertor_->Decode(data, field_value);
    return Status::OK;
}

template<class T>
Status BatchRead(SegmentContext* segment_ctx) {
    std::vector<DocContext>& doc_ctxs = *(segment_ctx->MutableDocContext());
    for (size_t idx = 0; idx < segment_ctx->DocIds().size(); ++idx) {
        if (doc_ctxs[idx].filtered) {
            continue;
        }

        util::FieldValue* field_value =
            segment_ctx->ArenaCtx().CreateOnArena<util::FieldValue>();
        doc_ctxs[idx].field2attr.emplace(field_id_, field_value);

        Status ret = Read_(segment_ctx->DocIds()[idx], field_value);
        if (ret != Status::OK) {
            return ret;
        }
    }

    return Status::OK;
}

}
}